import os.path
from datetime import datetime, timedelta
from urllib2 import urlopen, URLError, HTTPError

from django.conf import settings
from django.db import connection
from django.test.client import Client

from linkcheck.models import Link, Url

 #This needs some kind of autodiscovery mechanism
from pidman.linkcheck_config import linklists

def check(recheck_after_hours, limit):
    check_internal(recheck_after_hours, limit)
    check_external(recheck_after_hours, limit)

def check_internal(recheck_after_hours, limit):
    compare_date = datetime.now() - timedelta(hours=recheck_after_hours) #.strftime("%Y-%m-%d %H:%M:%S")

    #select urls where still_exists = True AND last_checked <= compare_date AND DOES NOT begin with http:// or https://

    urls = Url.objects.filter(still_exists__exact='TRUE').exclude(last_checked__gt=compare_date).exclude(url__regex=r'^https?://')

    #if limit is specified set the limit
    if limit and limit > -1:
        urls = urls[:limit]

    for u in urls:
        uv = UrlValidator(u.url).verify_internal()
        u.status        = uv.status
        u.message       = uv.message
        u.last_checked  = datetime.now()
        u.save()


def check_external(recheck_after_hours, limit):
    compare_date = datetime.now() - timedelta(hours=recheck_after_hours) #.strftime("%Y-%m-%d %H:%M:%S")

    #select Urls which begin with (http:// or https://) and still_exists=TRUE AND last_checked lt compare date
    urls = Url.objects.filter(url__regex=r'https?://', still_exists__exact='TRUE').exclude(last_checked__gt=compare_date)

    #if limit is specified set the limit
    if limit and limit > -1:
        urls = urls[:limit]

    for u in urls:        
        uv = UrlValidator(u.url).verify_external()
        u.status        = uv.status
        u.message       = uv.message
        u.last_checked  = datetime.now()
        u.save()

def find():
    results = {}
    Url.objects.all().update(still_exists=False)
    for k,v in linklists.items():
        results[k] = v().get_linklist()
        for item in results[k]:
            for url in (item['urls']+item['images']):
                u, created = Url.objects.get_or_create(url=url[1])
                l, created = Link.objects.get_or_create(url=u, field=url[0], content_type=v.content_type(), object_id=item['object'].id)
                u.still_exists = True
                u.save()
    Url.objects.filter(still_exists=False).delete()
    return results


class UrlValidator():
    def __init__(self, uri):
        self.uri     = uri
        self.status  = False
        self.message = "Not Tested"

    def verify_internal(self):
        self.status = False
        self.message = 'Invalid URL'

        try:
            if not self.uri:
                self.message = 'Empty link'
            elif self.uri.startswith('mailto:'):
                self.status = None
                self.message = 'Email link (not checked)'
            elif self.uri.startswith('#'):
                self.status = None
                self.message = 'Link to same page (not checked)'
            elif self.uri.startswith('/media/'): #TODO fix hard-coded media self.uri
                if os.path.exists(settings.MEDIA_ROOT+self.uri[6:]): #TODO fix hard-coded media prefix length
                    self.message = 'Working document link'
                    self.status = True
                else:
                    self.message = 'Missing Document'
            elif self.uri.startswith('/'):
                response = Client().get(self.uri)
                if response.status_code == 200:
                    self.message = 'Working internal link'
                    self.status = True
                elif (response.status_code == 302 or response.status_code == 301):
                    self.status = None
                    self.message = 'Redirect %d' % (response.status_code, )
                else:
                    self.message = 'Broken internal link'
        except:
            pass
        
        return self

    def verify_external(self):
        self.status = False
        try:
            response = urlopen(self.uri.rsplit('#')[0]) # Remove URL fragment identifiers
            self.message = ' '.join([str(response.code), response.msg])
            self.status = True
        except HTTPError, e:
            if hasattr(e, 'code') and hasattr(e, 'msg'):
                self.message = ' '.join([str(e.code), e.msg])
            else:
                self.message = "Unknown Error"
        except URLError, e:
            if hasattr(e, 'reason'):
                self.message = 'Unreachable: '+str(e.reason)
            elif hasattr(e, 'code') and e.code!=301:
                self.message = 'Error: '+str(e.code)
            else:
                self.message = 'Redirect. Check manually: '+str(e.code)

        return self
